package com.hwy.widget.segment

import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.Log
import android.util.SparseArray
import android.view.MotionEvent
import android.view.ViewConfiguration
import androidx.annotation.ColorInt
import androidx.annotation.IntDef
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.util.forEach
import com.hwy.widget.R
import kotlin.math.abs
import kotlin.math.max

/**
 * =======================================
 * 作者: hewenyu
 * 日期: 2022/5/1 12:16
 * 说明:
 * =======================================
 */
class TabSegment @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatTextView(context, attrs, defStyleAttr) {

    companion object {

        private const val TAG = "TabSegment"

        /**
         * item大小根据内容自适应
         */
        const val MODEL_WRAPPER = 1

        /**
         * item大小固定
         */
        const val MODEL_ENSURE = 2

        @IntDef(MODEL_WRAPPER, MODEL_ENSURE)
        annotation class Model

    }

    /**
     * 控件圆角大小
     */
    private var tsCorner = 0F

    /**
     * item宽高模式
     */
    @Model
    private var tsItemModel = MODEL_WRAPPER

    /**
     * item高度
     */
    private var tsItemHeight = 0

    /**
     * item宽度
     */
    private var tsItemWidth = 0

    private var tsPaddingLeft = 0
    private var tsPaddingTop = 0
    private var tsPaddingRight = 0
    private var tsPaddingBottom = 0

    // region ----------------------------------- 分割线 -----------------------------------

    private val dividerPaint by lazy {
        Paint().apply {
            isAntiAlias = true
            isDither = true
            style = Paint.Style.FILL_AND_STROKE
        }
    }

    /**
     * 分割线宽度
     */
    private var tsDividerWidth = 0

    /**
     * 分割线高度
     */
    private var tsDividerHeight = 0

    /**
     * 分割线颜色
     */
    @ColorInt
    private var tsDividerColor = Color.TRANSPARENT

    // endregion --------------------------------------------------------------------------

    // region ----------------------------------- 文本绘制 -----------------------------------

    private val selectTextPaint by lazy {
        Paint().apply {
            isDither = true
            isAntiAlias = true
            textAlign = Paint.Align.CENTER
        }
    }

    private val normalTextPaint by lazy {
        Paint().apply {
            isDither = true
            isAntiAlias = true
            textAlign = Paint.Align.CENTER
        }
    }

    /**
     * 选中文本的大小
     */
    private var tsSelectTextSize = 0F

    /**
     * 选中文本颜色
     */
    @ColorInt
    private var tsSelectTextColor = Color.BLACK

    /**
     * 默认文本的大小
     */
    private var tsNormalTextSize = 0F

    /**
     * 默认的文本颜色
     *
     */
    @ColorInt
    private var tsNormalTextColor = Color.BLACK


    // endregion ---------------------------------------------------------------------------

    /**
     * 缓存每个item的位置
     */
    private val itemRectMap by lazy {
        SparseArray<Rect>()
    }

    /**
     * 用于计算的矩形
     */
    private val bounds by lazy {
        Rect()
    }

    // region ----------------------------------- Slider滑块 -----------------------------------

    /**
     * 滑块组件
     */
    var segmentSlider: AbstractSegmentSlider = SimpleSegmentSlider(this)

    /**
     * 滑块圆角
     */
    private var tsSliderCorner = 0F

    /**
     * 滑块颜色
     */
    private var tsSliderColor = Color.TRANSPARENT

    /**
     * 滑块动画时长
     */
    private var tsSliderDuration = 400

    /**
     * 当前选中的位置
     */
    private var currentIndex = 0

    // endregion ------------------------------------------------------------------------------

    // region ----------------------------------- 点击事件相关参数 -----------------------------------

    private val touchSlop by lazy {
        ViewConfiguration.get(context).scaledTouchSlop
    }

    /**
     * 记录down事件的区域
     */
    private var touchRect: Rect? = null

    private var lastX = 0F

    private var lastY = 0F

    /**
     * item点击事件回调
     */
    var onItemClickListener: OnItemClickListener? = null

    // endregion ----------------------------------------------------------------------------------

    private var adapter: Adapter? = null

    /**
     * 圆角裁剪路径
     */
    private val clipPath by lazy {
        Path()
    }

    init {
        initAttrs(attrs)
        setDefaultAttr()
    }

    /**
     * 初始化自定义属性
     */
    private fun initAttrs(attrs: AttributeSet?) {
        attrs?.run {
            try {
                val typeArray = context.obtainStyledAttributes(this, R.styleable.TabSegment)
                tsCorner = typeArray.getDimension(R.styleable.TabSegment_tsCorner, tsCorner)
                tsItemWidth =
                    typeArray.getDimensionPixelSize(R.styleable.TabSegment_tsItemWidth, tsItemWidth)
                tsItemHeight =
                    typeArray.getDimensionPixelSize(
                        R.styleable.TabSegment_tsItemHeight,
                        tsItemHeight
                    )
                tsItemModel = typeArray.getInt(R.styleable.TabSegment_tsItemModel, tsItemModel)
                tsPaddingLeft = typeArray.getDimensionPixelSize(
                    R.styleable.TabSegment_tsPaddingLeft,
                    tsPaddingLeft
                )
                tsPaddingTop = typeArray.getDimensionPixelSize(
                    R.styleable.TabSegment_tsPaddingTop,
                    tsPaddingTop
                )
                tsPaddingRight = typeArray.getDimensionPixelSize(
                    R.styleable.TabSegment_tsPaddingRight,
                    tsPaddingRight
                )
                tsPaddingBottom = typeArray.getDimensionPixelSize(
                    R.styleable.TabSegment_tsPaddingBottom,
                    tsPaddingBottom
                )
                tsDividerWidth = typeArray.getDimensionPixelSize(
                    R.styleable.TabSegment_tsDividerWidth,
                    tsDividerWidth
                )
                tsDividerHeight = typeArray.getDimensionPixelSize(
                    R.styleable.TabSegment_tsDividerHeight,
                    tsDividerHeight
                )
                tsDividerColor =
                    typeArray.getColor(R.styleable.TabSegment_tsDividerColor, tsDividerColor)
                tsSelectTextSize = typeArray.getDimension(
                    R.styleable.TabSegment_tsSelectTextSize,
                    tsSelectTextSize
                )
                tsSelectTextColor =
                    typeArray.getColor(R.styleable.TabSegment_tsSelectTextColor, tsSelectTextColor)
                tsNormalTextSize = typeArray.getDimension(
                    R.styleable.TabSegment_tsNormalTextSize,
                    tsNormalTextSize
                )
                tsNormalTextColor =
                    typeArray.getColor(R.styleable.TabSegment_tsNormalTextColor, tsNormalTextColor)
                tsSliderCorner =
                    typeArray.getDimension(R.styleable.TabSegment_tsSliderCorner, tsSliderCorner)
                tsSliderDuration =
                    typeArray.getInt(R.styleable.TabSegment_tsSliderDuration, tsSliderDuration)
                tsSliderColor =
                    typeArray.getColor(R.styleable.TabSegment_tsSliderColor, tsSliderColor)
                typeArray.recycle()
            } catch (e: Exception) {
                Log.d(TAG, "attrs parse failure")
            }
        }
    }

    /**
     * 设置默认属性
     */
    private fun setDefaultAttr() {
        selectTextPaint.textSize = tsSelectTextSize
        normalTextPaint.textSize = tsNormalTextSize
        isClickable = true
    }

    fun setAdapter(adapter: Adapter?) {
        this.adapter = adapter
        itemRectMap.clear()
        requestLayout()
        invalidate()
    }

    /**
     * 获取动画时长
     */
    fun getDuration(): Long {
        return tsSliderDuration.toLong()
    }

    fun getSliderColor(): Int {
        return tsSliderColor
    }

    fun getSliderCorner(): Float {
        return tsSliderCorner
    }

    /**
     * 计算触摸区域
     *
     * @param x 触摸事件x坐标
     * @param y 触摸事件y坐标
     * @return 返回触摸事件坐标对应的item矩形
     */
    private fun calculateTouchRegion(x: Float, y: Float): Rect? {
        itemRectMap.forEach { _, rect ->
            if (x > rect.left && x < rect.right && y > rect.top && y < rect.bottom) {
                return rect
            }
        }
        return null
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {

        event?.run {
            when (this.actionMasked) {
                MotionEvent.ACTION_DOWN -> {
                    if (segmentSlider.isAnimatorRunning()) {
                        // 动画过程中不触发新的点击事件
                        return super.onTouchEvent(event)
                    }
                    calculateTouchRegion(x, y)?.run {
                        val index = itemRectMap.indexOfValue(this)
                        // 重复点击判断
                        if (index != currentIndex) {
                            touchRect = this
                        }
                    }
                }
                MotionEvent.ACTION_MOVE -> {
                    touchRect?.run {
                        val distanceX = abs(x - lastX)
                        val distanceY = abs(y - lastY)
                        if (distanceX > touchSlop || distanceY > touchSlop) {
                            // 手指由滑动超过阈值,则认为不是点击事件
                            touchRect = null
                        }
                    }
                }
                MotionEvent.ACTION_UP -> {
                    touchRect?.run {
                        // 计算up事件所在的item区域
                        calculateTouchRegion(x, y)?.run {
                            if (this == touchRect) {
                                // 触发item点击事件
                                val index = itemRectMap.indexOfValue(this)
                                val oldIndex = currentIndex
                                currentIndex = index
                                // 启动动画
                                segmentSlider.startSlide(oldIndex, currentIndex)
                                // 回调点击事件
                                onItemClickListener?.onItemClick(oldIndex, currentIndex)
                            }
                            touchRect = null
                        }
                    }
                }
            }
            lastX = x
            lastY = y
        }
        return super.onTouchEvent(event)
    }

    /**
     * 获取index位置对应的矩形区域
     */
    fun getRectByIndex(index: Int): Rect? {
        return itemRectMap.get(index)
    }

    private fun getItemRectByIndex(index: Int): Rect {
        var rect = itemRectMap.get(index)
        if (rect == null) {
            rect = Rect()
            itemRectMap.put(index, rect)
        }
        return rect
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        var width = paddingLeft + paddingRight
        var height = paddingBottom + paddingTop
        var itemMaxHeight = 0
        adapter?.run {
            var startX = paddingLeft
            var startY = paddingTop
            for (index in 0 until getItemCount()) {
                val rect = getItemRectByIndex(index)
                when (tsItemModel) {
                    MODEL_WRAPPER -> {
                        // 宽高自适应
                        val itemStr = getItem(index)
                        // 计算文本宽高
                        val currentPaint =
                            if (currentIndex == index) selectTextPaint else normalTextPaint
                        currentPaint.getTextBounds(itemStr, 0, itemStr.length, bounds)
                        val itemWidth = bounds.width() + tsPaddingLeft + tsPaddingRight
                        val itemHeight = bounds.height() + tsPaddingTop + tsPaddingBottom

                        // 记录当前item绘制的位置
                        rect.set(startX, startY, startX + itemWidth, startY + itemHeight)
                        // 记录高度最高的item
                        itemMaxHeight = max(itemMaxHeight, itemHeight)
                        width += itemWidth
                        startX += itemWidth
                    }
                    MODEL_ENSURE -> {
                        // 宽高固定
                        val itemWidth = tsItemWidth + tsPaddingLeft + tsPaddingRight
                        val itemHeight = tsItemHeight + tsPaddingTop + tsPaddingBottom
                        rect.set(startX, startY, startX + itemWidth, startY + itemHeight)
                        itemMaxHeight = itemHeight
                        width += itemWidth
                        startX += itemWidth
                    }
                }
            }
            height += itemMaxHeight
        }
        setMeasuredDimension(width, height)
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        // 控件大小发生变化时，裁剪的path需要重新计算
        calculateCornerPath(w.toFloat(), h.toFloat(), tsCorner, clipPath)
    }

    override fun draw(canvas: Canvas) {
        clipCorner(canvas)
        super.draw(canvas)
    }

    /**
     * 裁剪圆角
     */
    private fun clipCorner(canvas: Canvas) {
        if (tsCorner > 0) {
            canvas.clipPath(clipPath)
        }
    }

    override fun onDraw(canvas: Canvas) {
        adapter?.run {
            // 绘制滑块
            segmentSlider.draw(currentIndex, canvas)
            for (index in 0 until getItemCount()) {
                val itemStr = getItem(index)
                val itemRect = getItemRectByIndex(index)
                if (currentIndex == index) {
                    // 绘制选中文本
                    drawSelectText(itemStr, itemRect, canvas)
                } else {
                    // 绘制默认文本
                    drawNormalText(itemStr, itemRect, index, canvas)
                }
                // 绘制分割线
                drawDivider(index, getItemCount(), canvas)
            }
        }
    }

    /**
     * 绘制分割线，默认绘制在item右侧
     * 1.currentIndex两端不绘制分割线
     * 2.最后一个item不绘制
     */
    private fun drawDivider(index: Int, itemCount: Int, canvas: Canvas) {
        if (index == currentIndex || index == currentIndex - 1 || index == itemCount - 1) {
            return
        }
        if (tsDividerWidth <= 0 || tsDividerHeight <= 0) {
            return
        }
        itemRectMap.get(index)?.run {
            dividerPaint.run {
                color = tsDividerColor
                strokeWidth = tsDividerWidth / 2F
            }
            val centerY = (height - paddingTop - paddingBottom) / 2F + paddingTop
            val startY = centerY - tsDividerHeight / 2F
            val endY = centerY + tsDividerHeight / 2F
            canvas.drawLine(right.toFloat(), startY, right.toFloat(), endY, dividerPaint)
        }
    }

    /**
     * 绘制默认显示的文本
     */
    private fun drawNormalText(itemStr: String, itemRect: Rect, index: Int, canvas: Canvas) {
        normalTextPaint.run {
            textSize = tsNormalTextSize
            color = tsNormalTextColor
            segmentSlider.run {
                if (isAnimatorRunning() && index == fromIndex) {
                    // 动画过程中时,上一个选中的index文本颜色需要渐变
                    color =
                        calculateColor(tsSelectTextColor, tsNormalTextColor, segmentSlider.fraction)
                }
            }
        }
        val baseline = normalTextPaint.fontMetrics.run {
            (descent - ascent) / 2F - descent
        }
        val itemWidth = itemRect.width() - tsPaddingLeft - tsPaddingRight
        val itemHeight = itemRect.height() - tsPaddingTop - tsPaddingBottom

        canvas.drawText(
            itemStr,
            itemRect.left + tsPaddingLeft + itemWidth / 2F,
            itemRect.top + tsPaddingTop + itemHeight / 2F + baseline,
            normalTextPaint
        )
    }

    /**
     * 绘制选中的文本
     */
    private fun drawSelectText(itemStr: String, itemRect: Rect, canvas: Canvas) {
        selectTextPaint.run {
            color = if (segmentSlider.isAnimatorRunning()) {
                // 动画过程中时，目标item文本颜色渐变计算
                calculateColor(tsNormalTextColor, tsSelectTextColor, segmentSlider.fraction)
            } else {
                tsSelectTextColor
            }
        }
        val baseline = selectTextPaint.fontMetrics.run {
            (descent - ascent) / 2F - descent
        }
        val itemWidth = itemRect.width() - tsPaddingLeft - tsPaddingRight
        val itemHeight = itemRect.height() - tsPaddingTop - tsPaddingBottom
        canvas.drawText(
            itemStr,
            itemRect.left + tsPaddingLeft + itemWidth / 2F,
            itemRect.top + tsPaddingTop + itemHeight / 2F + baseline,
            selectTextPaint
        )
    }

    override fun onDetachedFromWindow() {
        segmentSlider.stopSlide()
        super.onDetachedFromWindow()
    }

    interface Adapter {

        /**
         * 获取Item数量
         */
        fun getItemCount(): Int

        /**
         * 获取item内容
         */
        fun getItem(position: Int): String

    }

    interface OnItemClickListener {

        /**
         * item点击事件回调
         */
        fun onItemClick(from: Int, to: Int)

    }

}